package com.circle.interests.biz.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.circle.common.core.constant.Constant;
import com.circle.common.core.enums.Switch;
import com.circle.common.core.exception.RRException;
import com.circle.interests.biz.entity.PrizeInfo;
import com.circle.interests.biz.mapper.PrizeInfoMapper;
import com.circle.interests.biz.request.PrizeInfoPageRequest;
import com.circle.interests.biz.request.PrizeInfoRequest;
import com.circle.interests.biz.service.IPrizeInfoService;
import com.circle.prize.library.api.enums.InventoryTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * <p>
 * 奖品信息 服务实现类
 * </p>
 *
 * @author generator
 * @since 2021-12-05
 */
@Slf4j
@Service
@Transactional
public class PrizeInfoServiceImpl extends ServiceImpl<PrizeInfoMapper, PrizeInfo> implements IPrizeInfoService {

    @Override
    public IPage<PrizeInfo> page(PrizeInfoPageRequest form) {
        Page<PrizeInfo> page = new Page<>();
        page.setCurrent(Convert.toLong(form.getPageNo(), Constant.PAGE_NO_1));
        page.setSize(Convert.toLong(form.getPageSize(), Constant.PAGE_SIZE_25));
        Wrapper<PrizeInfo> query = Wrappers.lambdaQuery(PrizeInfo.class)
                .like(StrUtil.isNotBlank(form.getName()), PrizeInfo::getName, form.getName())
                .orderByDesc(PrizeInfo::getId);
        return page(page, query);
    }

    @Override
    public PrizeInfo add(PrizeInfoRequest form) {
        String code = form.getCode();
        if (StrUtil.isBlank(code)) {
            code = IdUtil.getSnowflake().nextIdStr();
        }
        PrizeInfo entity = getByCode(code);
        if (entity != null) {
            throw new RRException("奖品编号重复");
        }
        entity = new PrizeInfo();
        BeanUtils.copyProperties(form, entity);
        entity.setId(null);
        entity.setCode(code);
        entity.setStatus(Switch.ON.getCode());
        entity.setRemain(entity.getTotal());
        if (StrUtil.isBlank(entity.getCode())) {
            entity.setCode(IdUtil.getSnowflake().nextIdStr());
        }
        save(entity);
        return entity;
    }

    @Override
    public List<PrizeInfo> list(Long categoryId) {
        return list(Wrappers.lambdaQuery(PrizeInfo.class)
                .eq(categoryId != null, PrizeInfo::getCategoryId, categoryId)
                .orderByDesc(PrizeInfo::getId));
    }

    @Override
    public void updateById(PrizeInfoRequest form) {
        PrizeInfo entity = getById(form.getId());
        if (entity == null) {
            throw new RRException("奖品不存在");
        }
        BeanUtils.copyProperties(form, entity);
        entity.setCode(null);
        updateById(entity);
    }

    @Override
    public void deleteById(Long id) {
        // todo 校验奖品是否已使用，使用了则不可删除
        removeById(id);
    }

    @Override
    public PrizeInfo getByCode(String code) {
        return getOne(Wrappers.lambdaQuery(PrizeInfo.class)
                .eq(PrizeInfo::getCode, code).last("limit 1"));
    }

    @Override
    public Boolean increment(PrizeInfo prizeInfo, Integer count) {
        if (count == null || count < 0) {
            throw new RRException("非法数量");
        }
        return modifyRemain(prizeInfo, count);
    }

    @Override
    public Boolean decrement(PrizeInfo prizeInfo, Integer count) {
        if (count == null || count < 0) {
            throw new RRException("非法数量");
        }
        return modifyRemain(prizeInfo, -count);
    }

    @Override
    public Boolean hasPrize(Long categoryId) {
        return null != getOne(Wrappers.lambdaQuery(PrizeInfo.class)
                .eq(PrizeInfo::getCategoryId, categoryId).last("limit 1"));
    }

    @Override
    public Boolean modifyInventory(Long prizeId, Integer count) {
        PrizeInfo prizeInfo = getById(prizeId);
        // 校验库存
        if (InventoryTypeEnum.UNLIMITED.getCode().equals(prizeInfo.getInventoryType())) {
            log.warn("不限量奖品{}无需调整库存{}", prizeId, count);
            return true;
        }
        if (prizeInfo.getRemain() + count < 0) {
            log.error("奖品库存调整失败，不满足调整条件，奖品id:{}总库存:{},余量:{},调整量:{}", prizeId, prizeInfo.getTotal(), prizeInfo.getRemain(), count);
            return false;
        }
        // todo ABA问题后面引入缓存再优化
        PrizeInfo update = new PrizeInfo();
        update.setTotal(prizeInfo.getTotal() + count);
        update.setRemain(prizeInfo.getRemain() + count);
        boolean result = update(update, Wrappers.lambdaUpdate(PrizeInfo.class)
                .eq(PrizeInfo::getId, prizeInfo.getId())
                .eq(PrizeInfo::getTotal, prizeInfo.getTotal())
                .eq(PrizeInfo::getRemain, prizeInfo.getRemain()));
        if (!result) {
            log.warn("库存并发调整失败");
        }
        return result;
    }

    private boolean modifyRemain(PrizeInfo prizeInfo, Integer count) {
        // 校验库存
        if (prizeInfo.getRemain() + count < 0 || prizeInfo.getRemain() + count > prizeInfo.getTotal()) {
            return false;
        }
        // todo ABA问题后面引入缓存再优化
        PrizeInfo update = new PrizeInfo();
        update.setRemain(update.getRemain() + count);
        return update(update, Wrappers.lambdaUpdate(PrizeInfo.class)
                .eq(PrizeInfo::getId, prizeInfo.getId())
                .eq(PrizeInfo::getRemain, prizeInfo.getRemain()));

    }

}
